{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "opened-florist",
   "metadata": {},
   "source": [
    "# Pegasos Quantum Support Vector Classifier\n",
    "\n",
    "There's another SVM based algorithm that benefits from the quantum kernel method. Here, we introduce an implementation of a another classification algorithm, which is an alternative version to the `QSVC` available in Qiskit Machine Learning and shown in the [\"Quantum Kernel Machine Learning\"](./03_quantum_kernel.ipynb) tutorial. This classification algorithm implements the Pegasos algorithm from the paper \"Pegasos: Primal Estimated sub-GrAdient SOlver for SVM\" by Shalev-Shwartz et al., see: https://home.ttic.edu/~nati/Publications/PegasosMPB.pdf.\n",
    "\n",
    "This algorithm is an alternative to the dual optimization from the `scikit-learn` package, benefits from the kernel trick, and yields a training complexity that is independent of the size of the training set. Thus, the `PegasosQSVC` is expected to train faster than QSVC for sufficiently large training sets.\n",
    "\n",
    "The algorithm can be used as direct replacement of `QSVC` with some hyper-parameterization."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "thirty-painting",
   "metadata": {},
   "source": [
    "Let's generate some data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "impressed-laser",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import make_blobs\n",
    "\n",
    "# example dataset\n",
    "features, labels = make_blobs(n_samples=20, n_features=2, centers=2, random_state=3, shuffle=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "moderate-yugoslavia",
   "metadata": {},
   "source": [
    "We pre-process the data to ensure compatibility with the rotation encoding and split it into the training and test datasets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "adolescent-composer",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "\n",
    "features = MinMaxScaler(feature_range=(0, np.pi)).fit_transform(features)\n",
    "\n",
    "train_features, test_features, train_labels, test_labels = train_test_split(\n",
    "    features, labels, train_size=15, shuffle=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "central-poverty",
   "metadata": {},
   "source": [
    "We have two features in the dataset, so we set a number of qubits to the number of features in the dataset.\n",
    "\n",
    "Then we set $\\tau$ to the number of steps performed during the training procedure. Please note that, there is no early stopping criterion in the algorithm. The algorithm iterates over all $\\tau$ steps.\n",
    "\n",
    "And the last one is the hyperparameter $C$. This is a positive regularization parameter. The strength of the regularization is inversely proportional to $C$. Smaller $C$ induce smaller weights which generally helps preventing overfitting. However, due to the nature of this algorithm, some of the computation steps become trivial for larger $C$. Thus, larger $C$ improve the performance of the algorithm drastically. If the data is linearly separable in feature space, $C$ should be chosen to be large. If the separation is not perfect, $C$ should be chosen smaller to prevent overfitting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "dying-dispatch",
   "metadata": {},
   "outputs": [],
   "source": [
    "# number of qubits is equal to the number of features\n",
    "num_qubits = 2\n",
    "\n",
    "# number of steps performed during the training procedure\n",
    "tau = 100\n",
    "\n",
    "# regularization parameter\n",
    "C = 1000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "improving-wilderness",
   "metadata": {},
   "source": [
    "The algorithm will run using:\n",
    "\n",
    "- The default fidelity instantiated in `FidelityQuantumKernel`\n",
    "- A quantum kernel created from `z_feature_map`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "automated-allergy",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.circuit.library import z_feature_map\n",
    "from qiskit_machine_learning.utils import algorithm_globals\n",
    "\n",
    "from qiskit_machine_learning.kernels import FidelityQuantumKernel\n",
    "\n",
    "from qiskit_machine_learning.state_fidelities import ComputeUncompute\n",
    "from qiskit.primitives import StatevectorSampler as Sampler\n",
    "\n",
    "sampler = Sampler()\n",
    "fidelity = ComputeUncompute(sampler=sampler)\n",
    "\n",
    "algorithm_globals.random_seed = 12345\n",
    "\n",
    "feature_map = z_feature_map(feature_dimension=num_qubits, reps=1)\n",
    "\n",
    "qkernel = FidelityQuantumKernel(fidelity=fidelity, feature_map=feature_map)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "attractive-stationery",
   "metadata": {},
   "source": [
    "The implementation `PegasosQSVC` is compatible with the `scikit-learn` interfaces and has a pretty standard way of training a model. In the constructor we pass parameters of the algorithm, in this case there are a regularization hyper-parameter $C$ and a number of steps.\n",
    "\n",
    "Then we pass training features and labels to the `fit` method, which trains a models and returns a fitted classifier.\n",
    "\n",
    "Afterwards, we score our model using test features and labels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "representative-thumb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PegasosQSVC classification test score: 1.0\n"
     ]
    }
   ],
   "source": [
    "from qiskit_machine_learning.algorithms import PegasosQSVC\n",
    "\n",
    "pegasos_qsvc = PegasosQSVC(quantum_kernel=qkernel, C=C, num_steps=tau)\n",
    "\n",
    "# training\n",
    "pegasos_qsvc.fit(train_features, train_labels)\n",
    "\n",
    "# testing\n",
    "pegasos_score = pegasos_qsvc.score(test_features, test_labels)\n",
    "print(f\"PegasosQSVC classification test score: {pegasos_score}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sustainable-empire",
   "metadata": {},
   "source": [
    "For visualization purposes we create a mesh grid of a predefined step that spans our minimum and maximum values we applied in MinMaxScaler. We also add some margin to the grid for better representation of the training and test samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "judicial-pottery",
   "metadata": {},
   "outputs": [],
   "source": [
    "grid_step = 0.2\n",
    "margin = 0.2\n",
    "grid_x, grid_y = np.meshgrid(\n",
    "    np.arange(-margin, np.pi + margin, grid_step), np.arange(-margin, np.pi + margin, grid_step)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "marine-constitution",
   "metadata": {},
   "source": [
    "We convert the grid to the shape compatible with the model, the shape should be `(n_samples, n_features)`.\n",
    "Then for each grid point we predict a label. In our case predicted labels will be used for coloring the grid."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "competitive-outdoors",
   "metadata": {},
   "outputs": [],
   "source": [
    "meshgrid_features = np.column_stack((grid_x.ravel(), grid_y.ravel()))\n",
    "meshgrid_colors = pegasos_qsvc.predict(meshgrid_features)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "former-constraint",
   "metadata": {},
   "source": [
    "Finally, we plot our grid according to the labels/colors we obtained from the model. We also plot training and test samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "monetary-knife",
   "metadata": {
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "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NYWp3bt3L/R91yZOnBiwz9eRVDfeeKMUh7AYDQQAgO18+63I8eO+X9ch1vXqBe3jp0+fLo888oh51PnIdFRTIIqOtcWkMMO4dY6U4kLLCgAARQkq9euLtGzpe9HXdbsgOHHihMybN08efvhh07Iyc+bMArfv2LGj7N27Vx577DETRlzFrfo+nZxt0aJF0rhxY4mPj5d9+/bJl19+KTfccINcdtllkpCQIB06dJCNGzf67Abas2ePeT5//nzp1KmTGenTvHlz0/oTCIQVAAD85WpRmTVLZMOGCxdd77ldgL377rvSsGFDadCggfTt21dmzJhhJlvzRUOEzvo7ZswY032ji4vONfPcc8/JtGnTZNu2bWbeE72tQf/+/WXVqlXy+eefS7169eSmm24y6wsyYsQIGTZsmGzevFnq168vvXv3NvPZXCq6gQAAKKpGjURSU4v9Y6dPn25CiurWrZtkZWXJp59+alpQ8lOpUiVT36Kz/+btvtF5Tl555RXTEuKSlpbmtc3rr79uWmD0M3r06OHzuDSouGpoRo8eLU2aNJHvvvvOBKtLQcsKAESpKnJWkiXb56Kvw3527Ngh69atM60WKi4uTu68804TYIpCJ8W76qqrvNYdPHhQHnjgAdOiot1AFSpUMF1P2kVUEM/9uKbRd02rfyloWQGAKKRBZIz8eNHtRkp1OSQliuWYUDjTp083XSueBbXaBaT1JpMnTzbhwt+p7/NO0KZdQD///LNMmjRJatWqZfbdrl27i96dWu+m7OLaZyBuEklYAYAoFC/nLyBaW6FdGXlt3y7St+9v28EWcnJy5K233pIJEyZIly5dvF7r2bOnzJkzRx566CGfLSiuWwxczOrVq03XkNapuO50ffjwYQkVwgoARLMQ1VygaBYvXixHjhyR+++//4IWlNtvv920uvgKKzrPysqVK+Wuu+4yLSU60scX7f55++23pVWrVnLs2DF54okn3DcfDAVqVgAAKCptgdIhvXkXXR8E06dPl86dO+fb1aNhZf369fL111/n+14dCaRDjK+88kq5/PLLL/o5GopSU1PlnnvukT//+c9mlFCo0LICAIC/dMI3dX5EzkW3C5D/+7//8/la69atCxy+3LZtW/nqq6+81t17771myatFixZmrhVPvXr18nru+VnaapP3s3X0UEHH4w/CCgAA/tKZaXfuDOkM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5vseXe+5vdIWC1/bh8t5qRMnTkitWrXMzcoK+q0qXITDd3UpUlJSpFq1anLDDTfI6tWrxc6ysrLMY6VKlSLq+yrMeYXb363c3FyZO3euaS3S7qBI+a4QvqI+rBw+fNj8xUxKSvJar8999f/ren+2D5fzatCggcyYMUMWLlwos2bNMne7bt++vfzwww8Srnx9V3r32FOnTkm40oAydepU+de//mUWvQB27NjRdPfZkf5Z0i64a665Rpo2bepzu3D4u1WU8wqXv1tbtmyRcuXKmfquhx56SD744ANp3LhxRHxXCG9hcddlFA/9Dcrztyj9x7RRo0by2muvyTPPPBPSY8OFFz9dPL+rXbt2yUsvvSRvv/222I3WeGgtw6pVqySSFPa8wuXvlv6Z0toubS16//33pX///qZGx1dgAYpL1LesXHbZZRIbGysHDx70Wq/Pq1atmu97dL0/24fLeeVVokQJadGihXz33XcSrnx9V1rsWLp0aYkkrVu3tuV3NXjwYFm8eLF88sknpoizIOHwd6so5xUuf7dKlixpRsy1bNnSjHrSou9JkyaF/XeF8Bf1YUX/cupfzOXLl7vXaROtPvfVV6vrPbdXOirA1/bhcl55aTeSNgtrl0O4CofvKlD0N2I7fVdaK6wXdO1KyMjIkDp16kTE91WU8wrXv1v6b0Z2dnbYfleIIKGu8LWDuXPnWvHx8dbMmTOtb775xvrTn/5kJSYmWgcOHDCv33PPPdZTTz3l3n716tVWXFyc9cILL1jbt2+3Ro0aZZUoUcLasmWLFc7nNXr0aGvp0qXWrl27rA0bNlh33XWXVapUKWvbtm2WXRw/ftzatGmTWfSP74svvmj+f+/eveZ1PR89L5fvv//eKlOmjPXEE0+Y72rKlClWbGystWTJEstO/D2vl156yVqwYIH17bffmj93jz76qBUTE2N9/PHHll08/PDDZgTMihUrrMzMTPfy66+/urcJx79bRTmvcPi7pcerI5p2795tff311+a5w+GwPvroo7D9rhA5CCvn/eMf/7CuuOIKq2TJkmbI7+eff+5+rUOHDlb//v29tn/33Xet+vXrm+11aGx6eroV7uc1ZMgQ97ZJSUnWTTfdZG3cuNGyE9eQ3byL6zz0Uc8r73tSUlLMef3ud78zw0jtxt/zeu6556wrr7zSXPAqVapkdezY0crIyLDsJL/z0cXz5x+Of7eKcl7h8HdrwIABVq1atcwxXn755db111/vDirh+l0hcjj0P6Fu3QEAAPAl6mtWAACAvRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACArRFWAACA2Nn/B9lSbWKHZm3bAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.figure(figsize=(5, 5))\n",
    "meshgrid_colors = meshgrid_colors.reshape(grid_x.shape)\n",
    "plt.pcolormesh(grid_x, grid_y, meshgrid_colors, cmap=\"RdBu\", shading=\"auto\")\n",
    "\n",
    "plt.scatter(\n",
    "    train_features[:, 0][train_labels == 0],\n",
    "    train_features[:, 1][train_labels == 0],\n",
    "    marker=\"s\",\n",
    "    facecolors=\"w\",\n",
    "    edgecolors=\"r\",\n",
    "    label=\"A train\",\n",
    ")\n",
    "plt.scatter(\n",
    "    train_features[:, 0][train_labels == 1],\n",
    "    train_features[:, 1][train_labels == 1],\n",
    "    marker=\"o\",\n",
    "    facecolors=\"w\",\n",
    "    edgecolors=\"b\",\n",
    "    label=\"B train\",\n",
    ")\n",
    "\n",
    "plt.scatter(\n",
    "    test_features[:, 0][test_labels == 0],\n",
    "    test_features[:, 1][test_labels == 0],\n",
    "    marker=\"s\",\n",
    "    facecolors=\"r\",\n",
    "    edgecolors=\"r\",\n",
    "    label=\"A test\",\n",
    ")\n",
    "plt.scatter(\n",
    "    test_features[:, 0][test_labels == 1],\n",
    "    test_features[:, 1][test_labels == 1],\n",
    "    marker=\"o\",\n",
    "    facecolors=\"b\",\n",
    "    edgecolors=\"b\",\n",
    "    label=\"B test\",\n",
    ")\n",
    "\n",
    "plt.legend(bbox_to_anchor=(1.05, 1), loc=\"upper left\", borderaxespad=0.0)\n",
    "plt.title(\"Pegasos Classification\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "imperial-promise",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h3>Version Information</h3><table><tr><th>Software</th><th>Version</th></tr><tr><td><code>qiskit</code></td><td>1.4.3</td></tr><tr><td><code>qiskit_machine_learning</code></td><td>0.9.0</td></tr><tr><th colspan='2'>System information</th></tr><tr><td>Python version</td><td>3.12.9</td></tr><tr><td>OS</td><td>Windows</td></tr><tr><td colspan='2'>Fri Jul 18 15:25:04 2025 Eastern Daylight Time</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of a Qiskit project</h3><p>&copy; Copyright IBM 2017, 2025.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import tutorial_magics\n",
    "\n",
    "%qiskit_version_table\n",
    "%qiskit_copyright"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
